﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using JigLibX.Collision;      
using JigLibX.Physics;
using Microsoft.Xna.Framework.Graphics;

namespace DARE
{
    public class CRender3D : ARenderable, ICloneable
    {

        #region fields

        private IPartitioner m_Partitioner;
        private CCameraMgr m_cameraMgr;
        private CSkyMgr m_skyMgr;
        //private CWorldCreator m_worldCreator;
        private CEffectMgr m_effectMgr;
        private CRayCast m_rayCast;
        private PhysicsSystem m_physicsSystem;
        private CScene m_scene;
        private CNode m_originNode = null;
        private bool m_enablePhysics;
        private EType m_defaultNodeType;
        private CGameObjectMgr m_gameObjectMgr;
        private ITerrainRenderer m_terrainRenderer;
        /// <summary>
        /// Associate a collision skin with a node, used when raycasting.
        /// </summary>
        private Dictionary<CollisionSkin, CNode> m_collisionNode = new Dictionary<CollisionSkin, CNode>();

        // contain all the nodes inside the partitioner and outside
        private Dictionary<string, CNode> m_nodes = new Dictionary<string, CNode>();
        private Dictionary<string, CNode> m_dynamicNodes = new Dictionary<string, CNode>();

        // display, should be moved from here ?
        private bool m_showNodeModel = false;
        private bool m_showCollision = false;
        private int m_basicModelSlices = 20;
        private FillMode m_fillMode;
        private FillMode m_collisionFillMode;


        private List<CSprite3D> m_drawSprite = new List<CSprite3D>();

		//HeightMap Info
        //private CHeightMapInfo m_heightMapInfo;
        private List<CTerrainInfo> m_terrainInfos;

        // keep a list of physics controller to update the Pre et Post moethods
        // also physicsController Clean
        internal List<IControllerUpdate> m_physicsControllers = new List<IControllerUpdate>();

        /// <summary>
        /// This is the referance node, used as parent by nodes that are not in a render.
        /// It has the same properties as the Origin node but it is not updated or draw.
        /// </summary>
        static internal readonly CNode RefNode = new CNode(EType.STATIC, false);

        #endregion

        #region properties

        public bool IsActive
        {
            get;
            internal set;
        }

        /// <summary>
        /// Don't work yet xD
        /// </summary>
        public EType DefaultNodeType
        {
            get { return m_defaultNodeType; }
            set { m_defaultNodeType = value; }
        }

        public int DynamicNodeNumber
        {
            get { return m_dynamicNodes.Count; }
        }

        public int NodeNumber
        {
            get { return m_nodes.Count; }
        }

        public int StaticNodeNumber
        {
            get { return m_nodes.Count - m_dynamicNodes.Count; }
        }

        public Dictionary<string, CNode> DynamicNodes
        {
            get { return m_dynamicNodes; }
        }

        //public CHeightMapInfo HeightMapInfo 
        //{ 
        //    get { return m_heightMapInfo; }
        //    set { m_heightMapInfo = value; }
        //}

        public List<CTerrainInfo> TerrainInfos
        {
            get { return m_terrainInfos; }
        }

        public Dictionary<string, CNode> Nodes
        {
            get { return m_nodes; }
        }

        public CScene Scene
        {
            get { return m_scene; }
            set { m_scene = value; }
        }

        public IPartitioner Partitioner
        {
            get { return m_Partitioner; }
        }

        //public bool ShowPartitioner
        //{
        //    get { return m_showPartitioner; }
        //    set { m_showPartitioner = value; }
        //}

        public CCameraMgr CameraMgr
        {
            get { return m_cameraMgr; }
            set { m_cameraMgr = value; }
        }

        public CSkyMgr SkyMgr
        {
            get { return m_skyMgr; }
            set { m_skyMgr = value; }
        }

        public CGameObjectMgr GameObjectMgr
        {
            set { m_gameObjectMgr = value; }
            get { return m_gameObjectMgr; }
        }

        /// <summary>
        /// Yes you can !
        /// </summary>
        public int BasicModelSlices
        {
            get { return m_basicModelSlices; }
            set
            {
                if (value >= 4)
                {
                    foreach (CNode node in m_nodes.Values)
                        if (node.Entity != null && node.Entity.Model != null && (node.Entity.Model as CBasicModel != null))
                            ((CBasicModel)node.Entity.Model).Slices = value;
                    m_basicModelSlices = value;
                }
            }
        }

        /// <summary>
        /// Return the origin node, this node cant be deleted and moved.
        /// This is the parent of your world ! :D
        /// </summary>
        public CNode OriginNode
        {
            get { return m_originNode; }
        }

        /// <summary>
        /// Return a dictionnary containing a CollisionSkin and a CNode.
        /// This use by raycast to associate the collision with the node.
        /// </summary>
        public Dictionary<CollisionSkin, CNode> CollisionNode
        {
            get { return m_collisionNode; }
        }

        ///// <summary>
        ///// Get the WorldCreator.
        ///// <see cref="CWorldCreator"/>
        ///// </summary>
        //public CWorldCreator WorldCreator
        //{
        //    get { return m_worldCreator; }
        //}

        /// <summary>
        /// Get the RayCast class, used to intersect objects.
        /// <see cref="CRayCast"/>
        /// </summary>
        public CRayCast RayCast
        {
            get { return m_rayCast; }
        }

        /// <summary>
        /// Apply to all nodes in the render, show their origin.
        /// You can individualy hide a node model by calling HideModelNode in the node class.
        /// </summary>
        public bool ShowModelNode
        {
            get { return m_showNodeModel; }
            set { m_showNodeModel = value; }
        }

        public bool ShowCollision
        {
            get { return m_showCollision; }
            set
            {
                if (m_showCollision != value)
                {
                    m_showCollision = value;
                    foreach (CNode node in m_nodes.Values)
                        if (node.Entity != null && node.Entity.Physic != null && node.Entity.Physic.BasicModel != null)
                            node.Entity.Physic.BasicModel.Show = value;
                }
            }
        }

        public FillMode FillMode
        {
            get { return m_fillMode; }
            set
            {
                if (m_fillMode != value)
                {
                    m_fillMode = value;
                    foreach (CNode node in m_nodes.Values)
                        if (node.Entity != null && node.Entity.Model != null)
                        {
                            node.Entity.Model.FillMode = value;
                            node.Entity.Model.CullMode = value == FillMode.WireFrame ? CullMode.None : CullMode.CullCounterClockwiseFace;
                        }
                }
            }
        }

        public FillMode CollisionFillMode
        {
            get { return m_collisionFillMode; }
            set
            {
                if (m_collisionFillMode != value)
                {
                    m_collisionFillMode = value;
                    foreach (CNode node in m_nodes.Values)
                        if (node.Entity != null && node.Entity.Physic != null && node.Entity.Physic.BasicModel != null)
                        {
                            node.Entity.Physic.BasicModel.FillMode = value;
                            node.Entity.Physic.BasicModel.CullMode = value == FillMode.WireFrame ? CullMode.None : CullMode.CullCounterClockwiseFace;
                        }
                }
            }
        }

        public CEffectMgr EffectMgr
        {
            get { return m_effectMgr; }
            set { m_effectMgr = value; }
        }

        public PhysicsSystem PhysicsSystem
        {
            get { return m_physicsSystem; }
        }

        public bool EnalbePhysics
        {
            get { return m_enablePhysics; }
            set
            {
                //if (value == m_enablePhysics)
                //    return;
                m_enablePhysics = value;

                foreach (CNode node in m_nodes.Values)
                    if (node.Entity != null && node.Entity.Physic != null && node.Entity.Physic.Body != null)
                    {
                        if (m_enablePhysics)
                        {
                            node.Entity.Physic.Body.EnableBody();
                            node.Entity.Physic.Body.SetActive();
                        }
                        else
                        {
                            node.Entity.Physic.Body.DisableBody();
                            //node.Entity.Physic.Body.IsActive = false;
                        }
                    }
            }
        }

        #endregion

        #region ctor

        /// <summary>
        /// Default world dimension: 0 50 0 50 -50 0 2
        /// </summary>
        public CRender3D()
        {
            Initialize("render3d", new COctree(0, 50, 0, 50, -50, 0, 2));
        }

        public CRender3D(string renderName)
        {
            Initialize(renderName, new COctree(0, 50, 0, 50, -50, 0, 2));
        }

        public CRender3D(string renderName, IPartitioner partitioner)
        {
            Initialize(renderName, partitioner);
        }

        private void Initialize(string renderName, IPartitioner partitioner)
        {
            if (renderName == null)
                throw new Exception("The render name cannot be null !");
            if (partitioner == null)
                throw new Exception("The space partitioner cannot be null !");
            m_gameObjectMgr = new CGameObjectMgr(this);
            m_name = renderName;
            m_originNode = new CNode("origin");
            m_originNode.Render3D = this;
            m_originNode.SetParent(null as CNode);
            m_Partitioner = partitioner;
            m_cameraMgr = new CCameraMgr();
            m_skyMgr = new CSkyMgr(this);
            //m_worldCreator = new CWorldCreator(this);
            m_effectMgr = new CEffectMgr(this);
            m_rayCast = new CRayCast(this);
            m_defaultNodeType = EType.STATIC;
            FillMode = FillMode.Solid;
            m_showCollision = false;
            m_collisionFillMode = FillMode.WireFrame;
            m_drawSprite.Capacity = 100;
            m_terrainInfos = new List<CTerrainInfo>();
            ResetPhysics();
        }

        public void ResetPhysics()
        {
            m_physicsSystem = new PhysicsSystem();
            m_physicsSystem.CollisionSystem = new CollisionSystemSAP();

            m_physicsSystem.EnableFreezing = true;
            m_physicsSystem.SolverType = JigLibX.Physics.PhysicsSystem.Solver.Normal;
            m_physicsSystem.CollisionSystem.UseSweepTests = true;
            m_physicsSystem.NumCollisionIterations = 5;
            m_physicsSystem.NumContactIterations = 10;
            m_physicsSystem.NumPenetrationRelaxtionTimesteps = 1;
            m_physicsSystem.Gravity = new Vector3(0.0f, -10.0f, 0.0f);
        }

        public object Clone()
        {
            CRender3D render3d = new CRender3D(m_name, m_Partitioner);

            // ----- clone -----
            // game object
            // node
            //m_originNode.

            // camera
            // sky
            // effect
            // terrain info
            
            return render3d;
        }

        #endregion

        #region update/draw

        public override void Update(GameTime gameTime)
        {
            //CDare.Instance.DebugMgr.DebugConsole.WriteLine("updating " + m_name + "...");

            float timeStep = CDare.Instance.Config.Time.TimeStep;
            CCamera activeCamera = m_cameraMgr.ActiveCamera;

            // update physics, camera, sky
            // physics controller pre update
            foreach (IControllerUpdate controller in m_physicsControllers)
                controller.PreUpdate(timeStep * 2);
            // physics update
            m_physicsSystem.Integrate(timeStep * 2);
            // physics controller post update
            foreach (IControllerUpdate controller in m_physicsControllers)
                controller.PostUpdate(timeStep * 2);
            m_cameraMgr.Update(gameTime); // should be updated for each camera
            m_skyMgr.Update(gameTime);
            m_originNode.Update(gameTime);
            // process the nodes by the camera's frustrum in the partitionner
            if (activeCamera != null)
                m_Partitioner.Process(m_cameraMgr.GetRenderingCameraList());
            // dynamic nodes are always updated
            foreach (CNode node in m_dynamicNodes.Values)
                if (node.IsActive)
                    node.Update(gameTime);
            // update static nodes only if it's visible
            foreach (IPartitionerItem item in m_Partitioner.FrontToBack)
                ((CNode)item).Update(gameTime);
            foreach (IPartitionerItem item in m_Partitioner.Regular)
                ((CNode)item).Update(gameTime);
            foreach (IPartitionerItem item in m_Partitioner.BackToFront)
                ((CNode)item).Update(gameTime);
            if (m_gameObjectMgr != null)
                m_gameObjectMgr.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            lock (CDare.Instance.GraphicsDevice)
            {
                ContainmentType cType;

                // draw the sky
                m_skyMgr.Draw(gameTime);
                // draw the partitionner
                m_Partitioner.Draw();
                // add each dynamic nodes in the camera's frustrum to the render lists
                foreach (CNode node in m_dynamicNodes.Values)
                {
                    if (node.IsActive)
                    {
                        cType = m_cameraMgr.ActiveCamera.Frustrum.Contains(node.AABB);
                        if ((cType == ContainmentType.Intersects || cType == ContainmentType.Contains))
                            m_Partitioner.AddProcessItem(m_cameraMgr.ActiveCamera.Position, node);
                    }
                }
                // draw the nodes
                m_originNode.Draw(gameTime);
                foreach (IPartitionerItem item in m_Partitioner.FrontToBack)
                    ((CNode)item).Draw(gameTime);
                foreach (IPartitionerItem item in m_Partitioner.Regular)
                    ((CNode)item).Draw(gameTime);
                foreach (IPartitionerItem item in m_Partitioner.BackToFront)
                    ((CNode)item).Draw(gameTime);
                // draw the sprite3d here
                foreach (CSprite3D sprite in m_drawSprite)
                    sprite.Draw(gameTime);
                m_drawSprite.Clear();
            }
        }

        #endregion

        #region sprite

        /// <summary>
        /// Should not order transparent text...
        /// </summary>
        /// <param name="sprite"></param>
        internal void DrawSprite(CSprite3D sprite)
        {
            int begin = 0;
            int middle = 0;
            int end = m_drawSprite.Count - 1;
            float dist = 0;

            sprite.cameraDistance = Vector3.DistanceSquared(m_cameraMgr.ActiveCamera.Position, sprite.Position);
            while (begin < end)
            {
                middle = (begin + end) / 2;
                dist = m_drawSprite[middle].cameraDistance;

                if (sprite.cameraDistance < dist)
                    begin = middle + 1;
                else if (sprite.cameraDistance > dist)
                    end = middle - 1;
                else
                    break;
            }
            m_drawSprite.Insert(begin, sprite);
        }

        #endregion

        #region node

        public CNode GetNode(string name)
        {
            return m_nodes.ContainsKey(name) ? m_nodes[name] : null;
        }

        public bool Contains(string nodeName)
        {
            return m_nodes.ContainsKey(nodeName);
        }

        public bool Contains(CNode node)
        {
            return m_nodes.ContainsKey(node.Name);
        }

        public CNode AddNode(CNode node)
        {
            // should set the parent to origin node but if it already had a parent...
            // ========== because the origin of the node was still "0" =======
            if (node.Parent.Equals(RefNode))
                node.SetParent(m_originNode);
            // ===============================================
            return _AddNode(node);
        }

        private CNode _AddNode(CNode node)
        {
            foreach (CNode child in node.Children.Values)
                _AddNode(child);

            if (m_nodes.ContainsKey(node.Name))
                throw new Exception("cannot add node " + node.Name + " already exists in render !");

            if (node.Render3D == null)
            {
                node.Render3D = this;
                if (node.Entity != null && node.Entity.Physic != null)
                    node.Entity.Physic.Body.EnableBody();
            }
            else if (!node.Render3D.Equals(this))
            {
                node.Render3D.RemoveNode(node);
                node.Render3D = this;
                if (node.Entity != null && node.Entity.Physic != null)
                    node.Entity.Physic.Body.EnableBody();
            }

            // current render 3d settings
            if (node.Entity != null)
            {
                // ================================================================
                if (node.Entity.Physic != null)
                {
                    CPhysicTerrain physicTerrain = null;

                    if ((physicTerrain = node.Entity.Physic as CPhysicTerrain) != null)
                        m_terrainInfos.Add(physicTerrain.TerrainInfo);

                    if (node.Entity.Physic.Skin != null)
                        PhysicsSystem.CollisionSystem.AddCollisionSkin(node.Entity.Physic.Skin);
                    if (node.Entity.Physic.Body != null)
                        node.Entity.Physic.Body.EnableBody();
                }
                // ================================================================
                if ((node.Entity.Physic != null) && (node.Entity.Physic.BasicModel != null))
                {
                    node.Entity.Physic.BasicModel.Show = ShowCollision;
                    node.Entity.Physic.BasicModel.FillMode = CollisionFillMode;
                    node.Entity.Physic.BasicModel.CullMode = CollisionFillMode == FillMode.WireFrame ? CullMode.None : CullMode.CullCounterClockwiseFace;
                }
                if (node.Entity.Model != null)
                {
                    node.Entity.Model.FillMode = FillMode;
                    node.Entity.Model.CullMode = FillMode == FillMode.WireFrame ? CullMode.None : CullMode.CullCounterClockwiseFace;
                }
            }

            if (node.Type == EType.STATIC)
                m_Partitioner.AddItem(node);
            else
                m_dynamicNodes.Add(node.Name, node);
            m_nodes.Add(node.Name, node);
            //((IPartitionerItem)node).eItemMoved += new EventHandler(ListenerItemMoved);
            //((IPartitionerItem)node).eItemStopped += new EventHandler(ListenerItemStopped);
            return node;
        }

        /// <summary>
        /// node cannot be null
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public CNode RemoveNode(CNode node)
        {
            foreach (CNode child in node.Children.Values)
                RemoveNode(child);
            if (!node.Equals(m_originNode))
            {
                if (node.Type == EType.STATIC)
                    m_Partitioner.RemoveItem(node);
                else
                    m_dynamicNodes.Remove(node.Name);
                m_nodes.Remove(node.Name);

                // ================================================================
                if (node.Entity.Physic != null)
                {
                    CPhysicTerrain physicTerrain = null;

                    if ((physicTerrain = node.Entity.Physic as CPhysicTerrain) != null)
                        m_terrainInfos.Remove(physicTerrain.TerrainInfo);

                    if (node.Entity.Physic.Skin != null)
                        PhysicsSystem.CollisionSystem.RemoveCollisionSkin(node.Entity.Physic.Skin);

                    // leak when creating differents physics objects and delete it ? RemoveBody ?
                    if (node.Entity.Physic.Body != null)
                        node.Entity.Physic.Body.DisableBody();
                }
                // ================================================================
            }
            return node;
        }

        public CNode RemoveNode(string name)
        {
            return RemoveNode(m_nodes[name]);
        }

        public void RemoveNodes()
        {
            RemoveNode(m_originNode);
        }

        public CNode CreateNode()
        {
            return CreateNode(EType.STATIC, true);
        }

        public CNode CreateNode(EType type)
        {
            return CreateNode(type, true);
        }

        public CNode CreateNode(EType type, bool createEntity)
        {
            return AddNode(new CNode(type, createEntity));
        }

        public CNode CreateNode(string name, EType type, bool createEntity)
        {
            return AddNode(new CNode(name, type, createEntity));
        }

        public CNode CreateNode(string name, EType type)
        {
            return AddNode(new CNode(name, type, true));
        }

        public CNode CreateNode(string name)
        {
            return AddNode(new CNode(name, EType.STATIC, true));
        }

        private void ListenerItemMoved(object sender, EventArgs e)
        {
            // not used
            //CNode node = sender as CNode;
        }

        private void ListenerItemStopped(object sender, EventArgs e)
        {
            // not used
            //CNode node = sender as CNode;
        }

        public List<CNode> GetChildrenOf(CNode node)
        {
            return GetChildrenOf(node.Name);
        }

        public List<CNode> GetChildrenOf(string nodeName)
        {
            List<CNode> childrenList = new List<CNode>();

            BuildChildrenList(m_nodes[nodeName], ref childrenList);
            return childrenList;
        }

        private void BuildChildrenList(CNode current, ref List<CNode> childrenList)
        {
            foreach (CNode node in current.Children.Values)
            {
                childrenList.Add(node);
                BuildChildrenList(node, ref childrenList);
            }
        }

        #endregion

        #region hide/show

        public void HighLightNodeModel(CNode node)
        {
            foreach (CNode nod in Nodes.Values)
                nod.NodeModel.Highlight(false);
            node.NodeModel.Highlight(true);
        }

        public void DisplayNodeHierarchy()
        {
            Console.WriteLine("\nWorld node hierarchy [active]:");
            DisplayNodeHierarchy(m_originNode, 0, 2);
            Console.WriteLine();
        }

        private void DisplayNodeHierarchy(CNode current, int level, int step)
        {
            bool active = false;

            if (Nodes.ContainsValue(current))
                active = true;
            Console.WriteLine(new String(' ', level) + current.Name + "  [" + active.ToString() + "]");
            foreach (CNode node in current.Children.Values)
                if (node.Children != null)
                    DisplayNodeHierarchy(node, level + step, step);
        }

        #endregion

        //#region world creation

        //public void CreateWorld(string worldPath)
        //{
        //    m_worldCreator.DestroyWorld();
        //    m_worldCreator.CreateWorld(worldPath);
        //}

        //public void DestroyWorld()
        //{
        //    m_worldCreator.DestroyWorld();
        //}

        //#endregion

        #region shadows

        public enum EShadowFromLightType
        {
            DIRECTIONNAL,
            POINT
        };

        /// <summary>
        /// 
        /// </summary>
        /// <param name="nodeToCompute"></param>
        /// <param name="lightType"></param>
        /// <param name="lightComponent">Can be the direction for the directional lighting or an offset for the point light</param>
        /// <returns></returns>
        //public Texture2D ComputeShadowMap(CNode nodeToCompute, EShadowFromLightType lightType = EShadowFromLightType.DIRECTIONNAL, Nullable<Vector3> lightComponent = null)
        //{
            /*RenderTarget2D rt = new RenderTarget2D(CDare.Instance.GraphicsDevice, 256, 256);
            CCamera shadowCamera = m_cameraMgr.RenderCameras["ShadowsCamera"];

            CCamera shadowCamera = m_cameraMgr.RenderCameras["ShadowsCamera"];

            switch (lightType)
            {
                case EShadowFromLightType.DIRECTIONNAL:
                        Vector4 lightPos = m_effectMgr.LightPosition;
                        shadowCamera.SetLookAt(nodeToCompute.Position - lightComponent.Value, nodeToCompute.Position);//new Vector3(lightPos.X, lightPos.Y, lightPos.Z);
                        //m_shadowCamera.Up = -Vector3.UnitZ;
                    break;
                case EShadowFromLightType.POINT:
                        lightPos = m_effectMgr.LightPosition;
                        shadowCamera.SetLookAt(new Vector3(lightPos.X, lightPos.Y, lightPos.Z) + lightComponent.Value, nodeToCompute.Position);
                 //       m_shadowCamera.Up = Vector3.UnitZ;
                    break;
            }
            CCamera cam = m_cameraMgr.ActiveCamera;
            m_cameraMgr.SetActiveCamera(shadowCamera);

            CDare.Instance.GraphicsDevice.SetRenderTarget(rt);
            CDare.Instance.GraphicsDevice.Clear(new Color(0, 0, 0, 0));
            //nodeToCompute.Entity.Model.Effect.Parameters["isRenderingShadows"].SetValue(true);
            nodeToCompute.Entity.Model.Draw(CDare.Instance.Config.Time.GameTime);
            //nodeToCompute.Entity.Model.Effect.Parameters["isRenderingShadows"].SetValue(false);



            CDare.Instance.GraphicsDevice.SetRenderTarget(null);

            m_cameraMgr.SetActiveCamera(cam);
            return rt;*/
       //     return null;
       // }

        //public Texture2D ComputeShadowMap(EShadowFromLightType lightType = EShadowFromLightType.DIRECTIONNAL, Nullable<Vector3> lightComponent = null)
        //{
            /*CCamera shadowCamera = m_cameraMgr.RenderCameras["ShadowsCamera"];
            if (lightComponent == null)
                lightComponent = Vector3.Zero;
            foreach (CNode node in m_nodes.Values)
            {
                if (node.Entity.Model != null)
                    node.Entity.Model.Effect.Parameters["isRenderingShadows"].SetValue(true);
                else
                    node.IsVisible = false;
             //   if (node.Entity.Physic != null && node.Entity.Physic.CollisionType == APhysicObject.ECollisionType.GROUND)
             //       node.IsVisible = false;
                switch (lightType)
                {
                    case EShadowFromLightType.DIRECTIONNAL:
                        Vector4 lightPos = m_effectMgr.LightPosition;
                        shadowCamera.Position = node.Position - lightComponent.Value;
                        shadowCamera.Target = node.Position;
                        //m_shadowCamera.Up = -Vector3.UnitZ;

                        break;
                    case EShadowFromLightType.POINT:
                        lightPos = m_effectMgr.LightPosition;
                        shadowCamera.Position = new Vector3(lightPos.X, lightPos.Y, lightPos.Z) + lightComponent.Value;
                        shadowCamera.Target = node.Position;
                        //       m_shadowCamera.Up = Vector3.UnitZ;
                        break;
                }
            }
            RenderTarget2D rt = new RenderTarget2D(CDare.Instance.GraphicsDevice, 1024, 1024);
            
            CCamera cam = m_cameraMgr.ActiveCamera;
            m_cameraMgr.SetActiveCamera(shadowCamera);
            
            CDare.Instance.GraphicsDevice.SetRenderTarget(rt);
            CDare.Instance.GraphicsDevice.Clear(new Color(0, 0, 0, 0));
            Draw(CDare.Instance.Config.Time.GameTime);
            



            CDare.Instance.GraphicsDevice.SetRenderTarget(null);
            foreach (CNode node in m_nodes.Values)
            {
                if (node.Entity.Model != null)
                    node.Entity.Model.Effect.Parameters["isRenderingShadows"].SetValue(false);
                else
                    node.IsVisible = true;
                //if (node.Entity.Physic != null && node.Entity.Physic.CollisionType == APhysicObject.ECollisionType.GROUND)
                //    node.IsVisible = true;
            }
            m_cameraMgr.SetActiveCamera(cam);
            return rt;*/
        //    return null;
        //}

        #endregion

    }
}
